/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal; import java.util.Iterator; import java.util.LinkedList; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.ShellAdapter; import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.events.ShellListener; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; /** * Manages a pool of shells. This can be used instead of creating and destroying * shells. By reusing shells, they will never be disposed until the pool goes away. * This is useful in situations where client code may have cached pointers to the * shells to use as a parent for dialogs. It also works around bug 86226 (SWT menus * cannot be reparented). * */ public class ShellPool { private int flags; /** * Parent shell (or null if none) */ private Shell parentShell; private LinkedList availableShells = new LinkedList(); private final static String CLOSE_LISTENER = "close listener"; //$NON-NLS-1$ private boolean isDisposed = false; private DisposeListener disposeListener = new DisposeListener() { public void widgetDisposed(DisposeEvent e) { WorkbenchPlugin.log(new RuntimeException("Widget disposed too early!")); //$NON-NLS-1$ } }; private ShellListener closeListener = new ShellAdapter() { public void shellClosed(ShellEvent e) { if (isDisposed) { return; } if (e.doit) { Shell s = (Shell)e.widget; ShellListener l = (ShellListener)s.getData(CLOSE_LISTENER); if (l != null) { s.setData(CLOSE_LISTENER, null); l.shellClosed(e); // The shell can 'cancel' the close by setting // the 'doit' to false...if so, do nothing if (e.doit) { Control[] children = s.getChildren(); for (int i = 0; i < children.length; i++) { Control control = children[i]; control.dispose(); } availableShells.add(s); s.setVisible(false); } else { // Restore the listener s.setData(CLOSE_LISTENER, l); } } } e.doit = false; } }; /** * Creates a shell pool that allocates shells that are children of the * given parent and are created with the given flags. * * @param parentShell parent shell (may be null, indicating that this pool creates * top-level shells) * @param childFlags flags for all child shells */ public ShellPool(Shell parentShell, int childFlags) { this.parentShell = parentShell; this.flags = childFlags; } /** * Returns a new shell. The shell must not be disposed directly, but it may be closed. * Once the shell is closed, it will be returned to the shell pool. Note: callers must * remove all listeners from the shell before closing it. */ public Shell allocateShell(ShellListener closeListener) { Shell result; if (!availableShells.isEmpty()) { result = (Shell)availableShells.removeFirst(); } else { result = new Shell(parentShell, flags); result.addShellListener(this.closeListener); result.addDisposeListener(disposeListener); } result.setData(CLOSE_LISTENER, closeListener); return result; } /** * Disposes this pool. Any unused shells in the pool are disposed immediately, * and any shells in use will be disposed once they are closed. * */ public void dispose() { for (Iterator iter = availableShells.iterator(); iter.hasNext();) { Shell next = (Shell) iter.next(); next.removeDisposeListener(disposeListener); next.dispose(); } availableShells.clear(); isDisposed = true; } }